// 模拟实现一个generate函数
/**
 * 青铜版
 * generate函数，返回的一个对象有 value, done属性，有next, throw方法 ，知道这些就可以开始模拟了
 *
 */

function myGenerate(array) {
  const len = array.length;
  let current = 0;
  let value = "";
  let done = false;
  return {
    done: false,
    value: "",
    next(arg) {
      if (done === true) {
        return {
          done,
          value: undefined,
        };
      }
      if (current < len) {
        if (arg) {
          value = arg;
        } else {
          value = array[current];
        }
        current++;
      } else {
        done = true;
      }
      return { done, value };
    },
  };
}

// 网上找来的，起码是铂金
(function () {
  var ContinueSentinel = {};

  var mark = function (genFun) {
    var generator = Object.create({
      next: function (arg) {
        return this._invoke("next", arg);
      },
    });
    genFun.prototype = generator;
    return genFun;
  };

  function wrap(innerFn, outerFn, self) {
    var generator = Object.create(outerFn.prototype);

    var context = {
      done: false,
      method: "next",
      next: 0,
      prev: 0,
      abrupt: function (type, arg) {
        var record = {};
        record.type = type;
        record.arg = arg;

        return this.complete(record);
      },
      complete: function (record, afterLoc) {
        if (record.type === "return") {
          this.rval = this.arg = record.arg;
          this.method = "return";
          this.next = "end";
        }

        return ContinueSentinel;
      },
      stop: function () {
        this.done = true;
        return this.rval;
      },
    };

    generator._invoke = makeInvokeMethod(innerFn, context);

    return generator;
  }

  function makeInvokeMethod(innerFn, context) {
    var state = "start";

    return function invoke(method, arg) {
      if (state === "completed") {
        return { value: undefined, done: true };
      }

      context.method = method;
      context.arg = arg;

      while (true) {
        state = "executing";

        var record = {
          type: "normal",
          arg: innerFn.call(self, context),
        };

        if (record.type === "normal") {
          state = context.done ? "completed" : "yield";

          if (record.arg === ContinueSentinel) {
            continue;
          }

          return {
            value: record.arg,
            done: context.done,
          };
        }
      }
    };
  }

  window.regeneratorRuntime = {};

  regeneratorRuntime.wrap = wrap;
  regeneratorRuntime.mark = mark;
})();

var _marked = regeneratorRuntime.mark(helloWorldGenerator);

function helloWorldGenerator() {
  return regeneratorRuntime.wrap(
    function helloWorldGenerator$(_context) {
      while (1) {
        switch ((_context.prev = _context.next)) {
          case 0:
            _context.next = 2;
            return "hello";

          case 2:
            _context.next = 4;
            return "world";

          case 4:
            return _context.abrupt("return", "ending");

          case 5:
          case "end":
            return _context.stop();
        }
      }
    },
    _marked,
    this
  );
}

var hw = helloWorldGenerator();

console.log(hw.next());
console.log(hw.next());
console.log(hw.next());
console.log(hw.next());

// async await 就是自动执行generate函数的next();
// 先定义一个异步函数，模拟请求数据
function getData(data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(data);
    }, 500);
  });
}

// generate函数,

function* testG() {
  const data1 = yield getData(1);
  console.log("data1", data1);

  const data2 = yield getData(2);
  console.log("data2", data2);

  const data3 = yield getData(2);
  console.log("data3", data3);

  return data3;
}

// 模拟一个async 函数

function myAsync(generateToFns) {
  return function anymous(...args) {
    const gens = generateToFns.call(this, ...args);
    return new Promise((resolve, reject) => {
      function step(method, payload) {
        let result;
        try {
          result = gens[method](payload);
        } catch (err) {
          reject(err);
        }

        const { done, value } = result;

        if (done) {
          return resolve(value);
        } else {
          return Promise.resolve(result).then(
            (val) => {
              step("next", val);
            },
            (err) => {
              step("throw", err);
            }
          );
        }
      }
      step("next");
    });
  };
}
